#pragma once
#include<iostream>
#include<unordered_map>
#include<string>
#include<cassert>
#include<vector>

class GlobalSymbolTable {

public:
    int deep;
    GlobalSymbolTable *next=nullptr;
    GlobalSymbolTable *pre=nullptr;
    GlobalSymbolTable(int _deep,GlobalSymbolTable *_pre):deep(_deep),pre(_pre){}
    // 存放可变变量
    std::unordered_map<std::string, std::string> table_var, shadow_table_var;
    // 存放常量变量
    std::unordered_map<std::string, int> table_const, shadow_table_const;
    // 存放数组维数信息
    std::unordered_map<std::string, std::vector<std::string>> table_arr_dim_info, shadow_table_arr_dim_info;
    // 记录已经被分配的符号
    std::unordered_map<std::string, int> table_symAlloced, shadow_table_symAlloced;
    // 存放数组指针信息
    std::unordered_map<std::string, int> table_arr_ptr_info, shadow_table_arr_ptr_info;

    int assigned_sym_num=0, shadow_assigned_sym_num;


    void insert_new_const(std::string key,int value,int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        if(block_deep==0 || (!p->contain_const(key) && !p->contain_var(key))){
            p->table_const[key]=value;
        }
        else{
            std::cout<<"Failed: Repeat define."<<std::endl;
            exit(EXIT_FAILURE);
        }
    }

    void insert_arr_dim_info(std::string key,std::vector<std::string> dim_info,int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        // 重复校验已有const或者ref进行过了，不需要再重复校验
        p->table_arr_dim_info[key]=dim_info;

    }

    void insert_arr_ptr_info(std::string key,int rest_dims_num, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        p->table_arr_ptr_info[key]=rest_dims_num;

    }

    int get_arr_ptr_info(std::string key, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_arr_ptr(key)){
            p=p->pre;
        }
        if(p!=nullptr)
            return p->table_arr_ptr_info[key];
        else assert(false);

    }

    GlobalSymbolTable *get_insert_p( int block_deep){
        GlobalSymbolTable *p=this;
        while(p->deep<block_deep){
            if(p->next==nullptr)p->next= new GlobalSymbolTable(p->deep+1,p);
            p=p->next;
        }
        return p;
    }

    bool isInConst(std::string key, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr){
            // 考虑全局变量时同时存在，应优先返回引用
            if(p->contain_var(key)){
                return false;
            }

            if(p->contain_const(key)){
                return true;
            }

            if(p->contain_var(key)){
                return false;
            }
            p=p->pre;
        }

        // 被编译程序语法正确时应该是必存在的
        assert(false);
    }

    // 仅在同类对象中调用
    bool contain_const(std::string key){
        return table_const.find(key)!=table_const.end();
    }

    bool contain_const_from(std::string key, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_const(key)){
            p=p->pre;
        }
        if(p!=nullptr)
            return true;
        else return false;
    }

    // 仅在同类对象中调用
    bool contain_var(std::string key){
        return table_var.find(key)!=table_var.end();
    }

    bool contain_var_from(std::string key, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_var(key)){
            p=p->pre;
        }
        if(p!=nullptr)
            return true;
        else return false;
    }

    // 仅在同类对象中调用
    bool contain_arr_ptr(std::string key){
        return table_arr_ptr_info.find(key)!=table_arr_ptr_info.end();
    }

    bool contain_arr_ptr_from(std::string key, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_arr_ptr(key)){
            p=p->pre;
        }
        if(p!=nullptr)
            return true;
        else return false;
    }

    void insert_new_var(std::string key, int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        std::string value="@"+key+"_"+std::to_string(block_deep);
        if(!p->contain_const(key) && !p->contain_var(key)){
            p->table_var[key]=value;
        }
        else{
            std::cout<<"Failed: Repeat define."<<std::endl;
            exit(EXIT_FAILURE);
        }
    }

    void insert_new_param(std::string key){
        GlobalSymbolTable *p=get_insert_p(1);
        std::string value="%"+key;
        p->table_var[key]=value;
    }

    // 编程辅助变量都存储在'1'层
    void record_alloc_sym(std::string ident, int block_deep ){
        std::string value="@"+ident+"_"+std::to_string(block_deep);
        GlobalSymbolTable *p=get_insert_p(1);
        p->table_symAlloced[value]=1;
    }

    bool sym_is_alloc(std::string ident,int block_deep){
        std::string sym_name="@"+ident+"_"+std::to_string(block_deep);
        GlobalSymbolTable *p=get_insert_p(1);
        return p->table_symAlloced.find(sym_name)!=p->table_symAlloced.end();
    }

    int const_get_value(std::string key,int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_const(key)){
            p=p->pre;
        }
        if(p!=nullptr)
            return p->table_const[key];
        else assert(false);
    }

    std::string var_get_ref(std::string key,int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_var(key)){
            if(block_deep==0 && p->deep==0 && p->contain_const(key)){
                return std::to_string(p->table_const[key]);
            }
            p=p->pre;
        }
        if(p!=nullptr)
            return p->table_var[key];
        else assert(false);
    }

    // 获取数组维数
    std::vector<std::string> get_arr_dim_info(std::string key,int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_arr_dim(key) ){
            p=p->pre;
        }
        if(p!=nullptr)
            return p->table_arr_dim_info[key];
        else assert(false);
    }

    // 仅在同类对象中调用
    bool contain_arr_dim(std::string key){
        if(table_arr_dim_info.find(key)!=table_arr_dim_info.end()){
            return true;
        }
        return false;
    }

    bool contain_arr_dim_from(std::string key,int block_deep){
        GlobalSymbolTable *p=get_insert_p(block_deep);
        while(p!=nullptr && !p->contain_arr_dim(key)){
            p=p->pre;
        }
        if(p!=nullptr)
            return true;
        else return false;
    }

    // 离开作用域后进行断链操作
    void break_link(int current_block_deep){
        GlobalSymbolTable *p=this;
        while(p!=nullptr && p->deep<current_block_deep){
            p=p->next;
        }
        if(p!=nullptr)
            p->next=nullptr;
    }

    void reset_and_clear_all(){
        next=nullptr;
    }

    void temp_save(){
        shadow_table_var=table_var;
        shadow_table_const=table_const;
        shadow_table_symAlloced=table_symAlloced;
        shadow_assigned_sym_num=assigned_sym_num;
        shadow_table_arr_dim_info=table_arr_dim_info;
        shadow_table_arr_ptr_info=table_arr_ptr_info;

        auto p=next;
        while(p!=nullptr){
            p->temp_save();
        }
    }

    void recover(){
        table_var=shadow_table_var;
        table_const=shadow_table_const;
        table_symAlloced=shadow_table_symAlloced;
        assigned_sym_num=shadow_assigned_sym_num;
        table_arr_dim_info=shadow_table_arr_dim_info;
        table_arr_ptr_info=shadow_table_arr_ptr_info;

        auto p=next;
        while(p!=nullptr){
            p->recover();
        }
    }


};

// 函数表
class FuncMetaTable {
    std::unordered_map<std::string, std::string> registered_funcs, shadow_registered_funcs;
public:
    void insert(std::string name, std::string type){
        registered_funcs[name]=type;
    }

    std::string get_type(std::string name){
        if(contain(name))
            return registered_funcs[name];
        else 
            assert(false);
    }

    bool contain(std::string name){
        return registered_funcs.find(name)!=registered_funcs.end();
    }

    void temp_save(){
        shadow_registered_funcs=registered_funcs;
    }

    void recover(){
        registered_funcs=shadow_registered_funcs;
    }

};